﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.Net.Mime;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Polaris.Common
{
    public class EmailService : IDisposable
    {
        private readonly SmtpClient _smtpClient;
        private bool _disposed;

        /// <summary>
        /// 初始化邮件服务
        /// </summary>
        /// <param name="host">SMTP服务器地址</param>
        /// <param name="port">端口号</param>
        /// <param name="enableSsl">启用SSL</param>
        /// <param name="userName">用户名</param>
        /// <param name="password">密码</param>
        /// <param name="timeout">超时时间（毫秒）</param>
        public EmailService(
            string host,
            int port = 587,
            bool enableSsl = true,
            string userName = null,
            string password = null,
            int timeout = 10000)
        {
            _smtpClient = new SmtpClient(host, port)
            {
                EnableSsl = enableSsl,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                UseDefaultCredentials = false,
                Timeout = timeout
            };

            if (!string.IsNullOrWhiteSpace(userName))
            {
                _smtpClient.Credentials = new NetworkCredential(userName, password);
            }
        }

        /// <summary>
        /// 发送电子邮件（同步）
        /// </summary>
        public void SendEmail(
            string from,
            IEnumerable<string> to,
            string subject,
            string body,
            bool isBodyHtml = true,
            IEnumerable<string> cc = null,
            IEnumerable<string> bcc = null,
            IEnumerable<EmailAttachment> attachments = null)
        {
            using var mailMessage = CreateMailMessage(from, to, subject, body, isBodyHtml, cc, bcc, attachments);
            _smtpClient.Send(mailMessage);
        }

        /// <summary>
        /// 异步发送电子邮件
        /// </summary>
        public async Task SendEmailAsync(
            string from,
            IEnumerable<string> to,
            string subject,
            string body,
            bool isBodyHtml = true,
            IEnumerable<string> cc = null,
            IEnumerable<string> bcc = null,
            IEnumerable<EmailAttachment> attachments = null)
        {
            using var mailMessage = CreateMailMessage(from, to, subject, body, isBodyHtml, cc, bcc, attachments);
            await _smtpClient.SendMailAsync(mailMessage);
        }

        private static MailMessage CreateMailMessage(
            string from,
            IEnumerable<string> to,
            string subject,
            string body,
            bool isBodyHtml,
            IEnumerable<string> cc,
            IEnumerable<string> bcc,
            IEnumerable<EmailAttachment> attachments)
        {
            var mailMessage = new MailMessage
            {
                From = new MailAddress(from),
                Subject = subject,
                Body = body,
                IsBodyHtml = isBodyHtml,
                Priority = MailPriority.Normal
            };

            AddAddresses(mailMessage.To, to);
            AddAddresses(mailMessage.CC, cc);
            AddAddresses(mailMessage.Bcc, bcc);
            AddAttachments(mailMessage.Attachments, attachments);

            return mailMessage;
        }

        private static void AddAddresses(MailAddressCollection collection, IEnumerable<string> addresses)
        {
            if (addresses == null) return;

            foreach (var address in addresses)
            {
                if (!string.IsNullOrWhiteSpace(address))
                {
                    collection.Add(address.Trim());
                }
            }
        }

        private static void AddAttachments(AttachmentCollection collection, IEnumerable<EmailAttachment> attachments)
        {
            if (attachments == null) return;

            foreach (var attachment in attachments)
            {
                var stream = new MemoryStream(attachment.Content);
                var mailAttachment = new Attachment(stream, attachment.FileName, attachment.MediaType);
                collection.Add(mailAttachment);
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (_disposed) return;

            if (disposing)
            {
                _smtpClient?.Dispose();
            }

            _disposed = true;
        }
    }

    public class EmailAttachment
    {
        public byte[] Content { get; }
        public string FileName { get; }
        public string MediaType { get; }

        public EmailAttachment(byte[] content, string fileName, string mediaType = null)
        {
            Content = content ?? throw new ArgumentNullException(nameof(content));
            FileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
            MediaType = mediaType ?? MediaTypeNames.Application.Octet;
        }

        public static EmailAttachment FromFile(string filePath, string mediaType = null)
        {
            var content = File.ReadAllBytes(filePath);
            var fileName = Path.GetFileName(filePath);
            return new EmailAttachment(content, fileName, mediaType);
        }
    }
}
